package kksoft.kkexpress.executor;

import kksoft.kkexpress.ExpressException;
import kksoft.kkexpress.resolver.ResolveContext;
import kksoft.kkexpress.resolver.Type;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

public class ExpressExecutor {
    private final ExpressContext context;
    private final ResolveContext rc;

    public ExpressExecutor(ExpressContext context, ResolveContext rc) {
        this.context = context;
        this.rc = rc;
    }

    private void eval(ResolveContext rc) throws ExpressException {
        for (int i = 0; i < rc.child.size(); i++) {
            ResolveContext c = rc.child.get(i);

            eval(c);
        }

        if (rc.type == Type.LITERAL_TYPE) {
            if (!context.varMap.containsKey(rc.sb.toString())) {
                throw new ExpressException("var not found: " + rc.sb.toString());
            }
            rc.value = context.varMap.get(rc.sb.toString());
            rc.hasEval = true;
        } else if (rc.type == Type.STRING_TYPE) {
            rc.value = rc.sb.toString();
            rc.hasEval = true;
        } else if (rc.type == Type.FUNC_TYPE) {
            if (!context.funcMap.containsKey(rc.sb.toString())) {
                throw new ExpressException("func not found: " + rc.sb.toString());
            }
            Object funcObj = context.funcMap.get(rc.sb.toString());

            List<Object> params = new ArrayList<Object>();
            for (int i = 0; i < rc.child.size(); i++) {
                ResolveContext c = rc.child.get(i);
                params.add(c.value);
            }

            Method method = funcObj.getClass().getMethods()[0];

            Object[] paramObjs = params.toArray();
            try {
                rc.value = method.invoke(funcObj, paramObjs);
                rc.hasEval = true;
            } catch (Throwable t) {
                throw new ExecuteException("发生错误: ", t);
            }
        } else if (rc.type == Type.UNKNOWN_TYPE) {
            throw new ExpressException("unknown type: " + rc.sb.toString());
        }
    }

    public void exec() throws ExpressException {
        eval(rc);
    }
}
